
#include "serversynch.h"

ServerLock::ServerLock(char* debugName, int lockIndex, Server* serv) {
	name = debugName;
	busy = false;
	ownerThread = NULL;
	waitingQueue = new List();
	lockID = lockIndex;
	server = serv;
	created = false;
}
ServerLock::~ServerLock() {
	delete waitingQueue;
}
void ServerLock::SAcquire(int userProgID) {
	if(created){
		//disable interupts
		IntStatus oldLevel = interrupt->SetLevel(IntOff);
		if (ownerThread == userProgID) { // I am lock owner
			//DEBUG('L', "%s: %s attempted to acquire lock but he is already the owner.\n", name, currentThread->getName());
			//restore interupts + return;
			(void) interrupt->SetLevel(oldLevel);
			return;
		}
		if (!busy) { // lock is availible
			//make if BUSY
			busy = true;
			//make myself the lock owner
			if (DebugIsEnabled('n')) {
				printf("Lock %s: addr %d now owns it.\n", name, userProgID);
			}
			ownerThread = userProgID;
			
			server->AddToReadyQueue(userProgID);
		}
		else { // lock not availible
			//add myself to the queue
			printf("Lock %s: addr %d adding itself to the waitingQueue.\n", name, userProgID);
			waitingQueue->Append((void*)userProgID);
		}
		(void) interrupt->SetLevel(oldLevel);
		return;
	}
	else{
		printf("Lock %d has not been created. Error\n",lockID);
	}
}
void ServerLock::SRelease(int userProgID, bool isWait) {
	if (created){
		//disable interrupts
		IntStatus oldLevel = interrupt->SetLevel(IntOff);
		
		if (ownerThread != userProgID) { // I'm not the lock owner
			//restore interrupts + return
			(void) interrupt->SetLevel(oldLevel);
			return;
		}
		if (DebugIsEnabled('n')) {
			printf("Addr %d is releasing lock %d\n",userProgID,lockID);
		}
		//remove 1 lock from the lock's wait queue
		if(!isWait){
			server->AddToReadyQueue(userProgID);
		}
		if (!waitingQueue->IsEmpty()){
		int nextThread = (int)waitingQueue->Remove();
			//put them in the ready queue
			//allow next thread to acquire the lock
			busy = false;
			(void) interrupt->SetLevel(oldLevel);
			SAcquire(nextThread);
			return;
		}
		else { // waiting queue is empty
			//make lock FREE
			busy = false;
			//clear lock ownership
			ownerThread = NULL;
		}
		//restore interrupts
		(void) interrupt->SetLevel(oldLevel);
	}
	else{
		printf("Lock %d has not been created. Error\n",lockID);
	}
}

void ServerLock::SCreateLock(){
	created = true;
}

bool ServerLock::SIsHeldByCurrentThread(int userProgID) {
	return (ownerThread == userProgID);
}

int ServerLock::getLockID(){
	return lockID;
}

ServerCondition::ServerCondition(char* debugName, int condIndex, Server* serv) {
	name = debugName;
	waitingLock = NULL;
	waitingQueue = new List();
	condID = condIndex;
	server = serv;
}

ServerCondition::~ServerCondition() {
	delete waitingQueue;
}

void ServerCondition::SWait(ServerLock* ServCondLock, int userProgID) { 
	//disable interrupts
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	if (ServCondLock == NULL) {
		//restore interrupts + return
		(void) interrupt->SetLevel(oldLevel);
		return;
	}
	if (waitingLock == NULL) {
		// This is first thread calling wait
		waitingLock = ServCondLock;
		//DEBUG('C', "%s: %s is now the waitingLock.\n", name, waitingLock->getName());
	}
	
	if (waitingLock != ServCondLock) {
		//print error msg
		if (DebugIsEnabled('n')) {
			printf("%s: %s requested wait but we are currently busy with another lock %s\n", name, ServCondLock->getName(),waitingLock->getName());
		}
		//restore interrupts + return
		(void) interrupt->SetLevel(oldLevel);
		return;
	}
	// ok to wait
	ServCondLock->SRelease(userProgID,true); // exited the monitor
	// add myself to C.V. wait Queue
	waitingQueue->Append((void*)userProgID);
	//restore interrupts
	(void) interrupt->SetLevel(oldLevel);
}
void ServerCondition::SSignal(ServerLock* ServCondLock, int userProgID) {
	//disable interrupts
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	
	// If no waiters, restore interrupts + return
	if (waitingLock != ServCondLock) { 
		printf("The locks are not the same, return\n");
		(void) interrupt->SetLevel(oldLevel);
	}
	else{
		// wake up 1 waiter
		//remove 1 thread from condition wait queue
		if(!waitingQueue->IsEmpty()){
			printf("waiting queue is not empty");
			int nextThread = (int)waitingQueue->Remove();
			server->AddToReadyQueue(nextThread);
		}
		else{ // no more waiting threads
			waitingLock = NULL;
		}
	}
	server->AddToReadyQueue(userProgID);
	//restore interrupts + return
	(void) interrupt->SetLevel(oldLevel);
}
void ServerCondition::SBroadcast(ServerLock* ServCondLock, int userProgID) {
	//if the wrong lock is called, return
	if (waitingLock != ServCondLock) { 
		printf("The locks are not the same, return\n");
	}
	else{
		if(waitingQueue->IsEmpty()){
			printf("The waiting queue is empty");
		}
		while (!waitingQueue->IsEmpty()) { // there are waiters
			int temp = (int)waitingQueue->Remove();
			server->AddToReadyQueue(temp);
		}
	}
	server->AddToReadyQueue(userProgID);
}
